package org.nebuloop.photo.organizer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.nebuloop.photo.organizer.dao.ImageDAO;
import org.nebuloop.photo.organizer.dao.ImageDAOImpl;
import org.nebuloop.photo.organizer.util.Config;

/**
 * Starts the Image organizer application
 */
public class App {

  private static final Logger LOGGER = Logger.getLogger(App.class.getCanonicalName());

  /**
   * Starts the Image Organizer application
   * 
   * @param args
   *          Command line arguments
   */
  public static void main(String[] args) {
    try {
      System.out.println("Starting Photo Organizer");
      LOGGER.info("Starting Photo Organizer");
      Options options = setupCommandLineOptions();

      CommandLineParser parser = new PosixParser();
      CommandLine cmd = parser.parse(options, args);

      if (!cmd.hasOption("input") || !cmd.hasOption("output") || !cmd.hasOption("conf") || !cmd.hasOption("d")) {
        LOGGER.severe("Required parameters not specified");
        HelpFormatter helpFormatter = new HelpFormatter();
        helpFormatter.printHelp("java " + App.class.getName(), options);
        System.exit(-1);
      }
      String inputDir = cmd.getOptionValue("input");
      String outputDir = cmd.getOptionValue("output");
      String configFile = cmd.getOptionValue("conf");
      String skippedDir = cmd.getOptionValue("d");

      CompositeConfiguration config = new CompositeConfiguration();
      config.addConfiguration(new SystemConfiguration());
      if (configFile.endsWith(".xml")) {
        config.addConfiguration(new XMLConfiguration(configFile));
      } else {
        config.addConfiguration(new PropertiesConfiguration(configFile));
      }
      Config.setConfiguraiton(config);
      boolean skipBadFiles = config.getBoolean("skip.bad-media", false);
      boolean preserveNonNumericFolderName = config.getBoolean("preserve.non-numeric.folder.name", true);

      LOGGER.log(Level.INFO, "Input Path : {0}", inputDir);
      LOGGER.log(Level.INFO, "Output Path : {0}", outputDir);

      File dir = new File(inputDir);

      final ImageDAO imageDao = ImageDAOImpl.getInstance(outputDir, config);
      imageDao.init();

      int maxFiles = config.getInt("max.files", -1);
      if (cmd.hasOption("m")) {
        maxFiles = Integer.parseInt(cmd.getOptionValue("m"));
      }
      final SequentialImageOrganizer imageOrganizer = (maxFiles > 0) ? new SequentialImageOrganizer(maxFiles)
          : new SequentialImageOrganizer();

      imageOrganizer.setSkipBadFiles(skipBadFiles);
      imageOrganizer.setDestinationDirectory(outputDir);
      imageOrganizer.setSkippedFilesDestinationDirectory(skippedDir);

      SkippedFileLogger skippedFileLogger = null;
      final String skippedFilesLog = config.getString("skip.bad.media.log.file", "skipped_files");
      FileOutputStream fout = new FileOutputStream(skippedFilesLog, true);
      PrintWriter skippedFileLog = new PrintWriter(fout);
      skippedFileLogger = new SkippedFileLogger(skippedFileLog);
      final boolean useCameraModel = config.getBoolean("use.camera.in.file", true);

      LOGGER.log(Level.INFO, "skipBadFiles = {0}", skipBadFiles);
      LOGGER.log(Level.INFO, "preserveNonNumericImageLeafFolderName = {0}", preserveNonNumericFolderName);
      imageOrganizer.init(imageDao, preserveNonNumericFolderName, useCameraModel, skippedDir);
      imageOrganizer.setSkippedFileLogger(skippedFileLogger);

      Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
          imageOrganizer.interrupt();
          try {
            Thread.sleep(1000);
          } catch (InterruptedException ex) {
            LOGGER.log(Level.WARNING, null, ex);
          }
        }
      });

      imageOrganizer.processDir(dir);

      imageDao.getDbImageCount();
      imageDao.close();
      LOGGER.log(Level.INFO, "Total files processed : {0}", imageOrganizer.getProcessedImagesCount());
      LOGGER.log(Level.WARNING, "Files skipped : {0}", imageOrganizer.getSkippedFileCount());

      skippedFileLogger.close();
      LOGGER.info("Done : Photo Organizer");

      System.out.println("Done : Photo Organizer");
    } catch (Exception ex) {
      LOGGER.log(Level.SEVERE, null, ex);
      ex.printStackTrace();
      System.exit(-1);
    }
  }

  private static Options setupCommandLineOptions() {
    // create Options object
    Options options = new Options();
    // add t option
    options.addOption("conf", true, "Configuration file");
    options.addOption("input", true, "Input Directory");
    options.addOption("output", true, "Output Directory");
    options.addOption("d", "bad-files-directory", true, "Skipped files Directory");
    options.addOption("m", "max-files", true, "Maximum files to process. Primarily used for debugging");
    return options;
  }
}
